#include "cores.h"
#include "callbacks.h"

int initClient(CoreCtx *ctx) {
    pj_status_t status;
    pjsua_config uaCfg = {0};
    pjsua_media_config mediaCfg = {0};
    pjsua_transport_config transportCfg = {0};

    status = pjsua_create();
    if (status != PJ_SUCCESS) {
        pjsua_perror(__FUNCTION__, "Error initializing pjsua", status);
        return status;
    }

    pjsua_config_default(&uaCfg);
    pjsua_media_config_default(&mediaCfg);
    
    uaCfg.user_agent = pj_str(ctx->settings.userAgent);

    uaCfg.cb.on_reg_started2 = &app_on_reg_started2;
	uaCfg.cb.on_reg_state2 = &app_on_reg_state2;
	uaCfg.cb.on_call_state = &app_on_call_state;
	uaCfg.cb.on_dtmf_digit = &app_on_dtmf_digit;
	uaCfg.cb.on_call_tsx_state = &app_on_call_tsx_state;

	uaCfg.cb.on_call_redirected = &app_on_call_redirected;

	uaCfg.cb.on_call_media_state = &app_on_call_media_state;
	uaCfg.cb.on_call_media_event = &app_on_call_media_event;
	uaCfg.cb.on_incoming_call = &app_on_incoming_call;
	uaCfg.cb.on_nat_detect = &app_on_nat_detect;
	uaCfg.cb.on_buddy_state = &app_on_buddy_state;
	uaCfg.cb.on_pager2 = &app_on_pager2;
	uaCfg.cb.on_pager_status2 = &app_on_pager_status2;
	uaCfg.cb.on_call_transfer_request2 = &app_on_call_transfer_request2;
	uaCfg.cb.on_call_transfer_status = &app_on_call_transfer_status;

	uaCfg.cb.on_call_replace_request2 = &app_on_call_replace_request2;
	uaCfg.cb.on_call_replaced = &app_on_call_replaced;

	uaCfg.cb.on_mwi_info = &app_on_mwi_info;

	uaCfg.cb.on_ip_change_progress = &app_on_ip_change_progress;
	
	uaCfg.srtp_secure_signaling = 0;

    if (ctx->settings.enableSTUN) {
        for (int i = 0; i < ctx->settings.num_STUN; i++) {
            uaCfg.stun_srv[i] = pj_str(ctx->settings.STUN[i]);
        }
        uaCfg.stun_srv_cnt = ctx->settings.num_STUN;
    }

    mediaCfg.enable_ice = ctx->account.ice;
    mediaCfg.no_vad = ctx->settings.vad ? PJ_FALSE : PJ_TRUE;
    mediaCfg.ec_tail_len = ctx->settings.ec && !ctx->settings.opusStereo ? 20: 0;

    int maxClockRate = 8000;
	int maxChannelCount = 1;
	int curPos = 0;

    for (int i = 0; i < ctx->settings.num_audioCodecs; i++) {
        char *copy = strdup(ctx->settings.audioCodecs[i]);
        char *part = strtok(copy, "/");
        int isOpus = strcmp(part, "opus");
        int clockRate = 0;
        if (isOpus) {
            clockRate = 24000;
        }else {
            part = strtok(NULL, "/");
            if (part) {
                clockRate = atoi(part);
            }
        }

        if (clockRate > maxClockRate) {
			maxClockRate = clockRate;
		}

		if (!ctx->settings.ec && !isOpus) {
			part = strtok(NULL, "/");
			if (part) {
				int channelCount = atoi(part);
				if (channelCount > maxChannelCount) {
					maxChannelCount = channelCount;
				}
			}
		}

		free(copy);
    }

	mediaCfg.clock_rate = maxClockRate;
	if (ctx->settings.opusStereo) {
		mediaCfg.channel_count = 2;
	}else {
		mediaCfg.channel_count = maxChannelCount;
	}

	if (ctx->settings.enbaleDNS) {
		for (int i = 0; i < ctx->settings.num_DNS; i++) {
			uaCfg.nameserver[i] = pj_str(ctx->settings.DNS[i]);
		}

		uaCfg.nameserver_count = ctx->settings.num_DNS;
	}

	if (ctx->settings.enableLog) {
		pjsua_logging_config log_cfg;
		pjsua_logging_config_default(&log_cfg);
		log_cfg.msg_logging = PJ_TRUE;
		log_cfg.decor |= PJ_LOG_HAS_CR;
		log_cfg.log_filename = pj_str(ctx->settings.logPath);
		status = pjsua_init(&uaCfg, &log_cfg, &mediaCfg);
	}else {
		status = pjsua_init(&uaCfg, NULL, &mediaCfg);
	}

	if (status != PJ_SUCCESS) {
		pjsua_destroy();
		pjsua_perror(__FUNCTION__, "Error initializing pjsua", status);
		return status;
	}

	status = pjsua_start();

	if (status != PJ_SUCCESS) {
		pjsua_destroy();
		pjsua_perror(__FUNCTION__, "Error starting pjsua", status);
		return status;
	}

	pjsip_cfg()->endpt.disable_rport = ctx->settings.rport ? PJ_FALSE : PJ_TRUE;

	pjsua_transport_config_default(&transportCfg);
	if (ctx->settings.sourcePort) {
		transportCfg.port = ctx->settings.sourcePort;
	}else {
		transportCfg.port = 0;
	}
	status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &transportCfg, &ctx->transportId);
	if (status != PJ_SUCCESS) {
		pjsua_destroy();
		pjsua_perror(__FUNCTION__, "Error creating transport", status);
		return status;
	}

	return status;
}

int addAccount(CoreCtx *ctx) {
	pjsua_acc_config acc_cfg;

	pjsua_acc_config_default(&acc_cfg);
	acc_cfg.ka_interval = ctx->account.keepAlive;
	if (ctx->settings.rtpPortMin > 0) {
		acc_cfg.rtp_cfg.port = ctx->settings.rtpPortMin;
		if (ctx->settings.rtpPortMax > ctx->settings.rtpPortMin) {
			acc_cfg.rtp_cfg.port_range = ctx->settings.rtpPortMax - ctx->settings.rtpPortMin;
		}
	}

	acc_cfg.reg_timeout = ctx->account.registerRefresh;
	acc_cfg.use_srtp = PJMEDIA_SRTP_DISABLED;

	if (!ctx->settings.enableSTUN) {
		acc_cfg.rtp_cfg.public_addr = pj_str("Auto");
	}

	acc_cfg.ice_cfg_use = PJSUA_ICE_CONFIG_USE_CUSTOM;
	acc_cfg.ice_cfg.enable_ice = ctx->account.ice ? PJ_TRUE: PJ_FALSE;
	acc_cfg.allow_via_rewrite = ctx->account.allowRewrite ? PJ_TRUE : PJ_FALSE;
	acc_cfg.allow_sdp_nat_rewrite = acc_cfg.allow_via_rewrite;
	acc_cfg.allow_contact_rewrite = acc_cfg.allow_via_rewrite ? 2 : PJ_FALSE;
	acc_cfg.contact_rewrite_method = PJSUA_CONTACT_REWRITE_UNREGISTER;
	acc_cfg.publish_enabled = PJ_FALSE;
	acc_cfg.transport_id = ctx->transportId;

	acc_cfg.cred_count = 1;
	if (strlen(ctx->account.authID)) {
		acc_cfg.cred_info[0].username = pj_str(ctx->account.authID);
	}else {
		acc_cfg.cred_info[0].username = pj_str(ctx->account.username);
	}
	acc_cfg.cred_info[0].realm = pj_str("*");
	acc_cfg.cred_info[0].scheme = pj_str("Digest");
	acc_cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
	acc_cfg.cred_info[0].data = pj_str(ctx->account.password);

	acc_cfg.proxy_cnt = ctx->account.num_proxies;
	for (int i = 0; i < acc_cfg.proxy_cnt; i++) {
		char *newProxy = malloc(MAX_STRING_LENGTH);
		memset(newProxy, 0, MAX_STRING_LENGTH);
		sprintf(newProxy, "sip:%s", ctx->account.proxies[i]);
		acc_cfg.proxy[i] = pj_str(newProxy);
	}

	char localURI[MAX_STRING_LENGTH] = {0};
	sprintf(localURI, "\"%s\" ", ctx->account.displayName);
	sprintf(localURI + strlen(localURI), "<sip:%s@%s>", ctx->account.username, ctx->account.domain);
	acc_cfg.id = pj_str(localURI);

	if (strlen(ctx->account.server) == 0) {
		acc_cfg.register_on_acc_add = PJ_FALSE;
	}else {
		char regURI[MAX_STRING_LENGTH] = {0};
		sprintf(regURI, "sip:%s", ctx->account.server);
		acc_cfg.reg_uri = pj_str(regURI);
	}

	pj_status_t status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &ctx->accountId);

	return status;
}

int makeCall(CoreCtx *ctx, char *pNumber) {
	if (!pjsua_acc_is_valid(ctx->accountId)) {
		return -1;
	}

	char formattedNumber[MAX_STRING_LENGTH] = {0};
	sprintf(formattedNumber, "<sip:%s@%s>", pNumber, ctx->account.domain);

	pj_status_t status = pjsua_verify_sip_url(formattedNumber);
	if (status != PJ_SUCCESS) {
		pjsua_perror(__FUNCTION__, "Error verifying SIP URL", status);
		return -1;
	}

	// pjsua_snd_dev_param params;
	// pjsua_snd_dev_param_default(&params);
	// params.capture_dev = 0;
	// params.playback_dev = 0;
	// status = pjsua_set_snd_dev2(&params);
	// if (status != PJ_SUCCESS) {
	// 	pjsua_perror(__FUNCTION__, "Error setting sound device", status);
	// 	return -1;
	// }

	pjsua_call_id call_id;

	pjsua_call_setting call_setting;
	pjsua_call_setting_default(&call_setting);
	call_setting.flag = 0;
	call_setting.vid_cnt = 0;

	pjsua_msg_data msg_data;
	pjsua_msg_data_init(&msg_data);

	pj_str_t pj_uri = pj_str(formattedNumber);
	status = pjsua_call_make_call(ctx->accountId, &pj_uri, &call_setting, NULL, &msg_data, &call_id);
	if (status != PJ_SUCCESS) {
		pjsua_perror(__FUNCTION__, "Error making call", status);
		return -1;
	}

	if (status == PJ_SUCCESS) {
		pjsua_call_info call_info;
		pjsua_call_get_info(call_id, &call_info);
		printf("Call id:%d\n", call_info.call_id);
	}

	return status;
}